﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SudokuPanel;
using System.IO;
using System.Xml;
using System.Collections;
using System.Threading;
using System.Diagnostics;

namespace SudokuImporter
{
    public partial class ImportForm : Form
    {
        ConfigInfo ci = ConfigInfo.Instance();

        public ImportForm()
        {
            InitializeComponent();
        }

        private void buttonClear_Click(object sender, EventArgs e)
        {
            listStrSudoku.Items.Clear();
            buttonImport.Enabled = false;
        }

        private void buttonExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void buttonOpen_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            //dlg.FileName = "d:\\puzzle.txt";
            dlg.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            String strFilename = dlg.FileName;
            StreamReader sr = new StreamReader(strFilename, Encoding.GetEncoding("GB2312"));
            String strContent = sr.ReadToEnd();
            sr.Close();
            strContent.Replace("\r","\n");
            String[] strs = strContent.Split(new char[]{'\n'});
            foreach (String strSudoku in strs)
            {
                if (strSudoku.Length != 81)
                    continue;
                listStrSudoku.Items.Add(strSudoku);
            }
            if (listStrSudoku.Items.Count > 0)
                buttonImport.Enabled = true;
            labelInfo.Text = "一共发现" + listStrSudoku.Items.Count + "个题目";
        }

        private void buttonImport_Click(object sender, EventArgs e)
        {
            dlx_solver dso = new dlx_solver();
            String str = listStrSudoku.Text;
            SudokuLibrary[] sls = new SudokuLibrary[ConfigInfo.lvlList.Length];
            for(int i=1;i<=ConfigInfo.lvlList.Length;++i)
            {
                String strlibfile=ci["lvl_"+i+"_xml","lvl"+i+".xml"];
                sls[i - 1] = new SudokuLibrary(ci["workpath"] + "\\" + strlibfile, i);
            }
            int cnt = 0;
            foreach(String strSudoku in listStrSudoku.Items)
            {
                if (dso.solution_count(strSudoku)==1)
                {
                    int lvl = get_lvl(strSudoku);
                    if (lvl > ConfigInfo.lvlList.Length)
                        lvl = ConfigInfo.lvlList.Length;
                    sls[lvl-1].insertSudoku(strSudoku,true);
                    ++cnt;
                }
            }
            labelInfo.Text = "一共成功导入" + cnt + "个题目";
        }

        private void listStrSudoku_ValueMemberChanged(object sender, EventArgs e)
        {
            if (listStrSudoku.Text.Length > 0)
                buttonImport.Enabled = true;
            else
                buttonImport.Enabled = false;
        }


        private int get_lvl(String str)
        {
            return solver.get_lvl(str);
            //puzzle pz = new puzzle();
            //pz.loadPuzzle(str);
            //solver so = new solver(pz);
            //dlx_solver dso = new dlx_solver();
            //so.strResult = dso.do_solve(str);
            //int i = 0;
            //while (so.run(1)) ;
            //foreach (operation.SolveHelper helper in Enum.GetValues(typeof(operation.SolveHelper)))
            //{
            //    foreach (List<operation> oplist in so.opsteps)
            //    {
            //        foreach (operation op in oplist)
            //        {
            //            if (operation.lvl(op.helper) > i)
            //                i = operation.lvl(op.helper);
            //        }
            //    }
            //}
            //return i;
        }
        private String get_lvl_str(int i)
        {
            if (i > 0 && i <= ConfigInfo.lvlList.Length)
            {
                return ConfigInfo.lvlList[i - 1];
            }
            return "";
        }
        private String get_lvl_str(String str)
        {
            int i = get_lvl(str);
            if (i > 0 && i <= ConfigInfo.lvlList.Length)
            {
                return ConfigInfo.lvlList[i - 1];
            }
            return "";
        }



        private void buttonImportFromFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            //dlg.FileName = "d:\\puzzle.txt";
            dlg.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            dlx_solver dso = new dlx_solver();
            String str = listStrSudoku.Text;
            SudokuLibrary[] sls = new SudokuLibrary[ConfigInfo.lvlList.Length];
            for (int i = 1; i <= ConfigInfo.lvlList.Length; ++i)
            {
                String strlibfile = ci["lvl_" + i + "_xml", "lvl" + i + ".xml"];
                sls[i - 1] = new SudokuLibrary(ci["workpath"] + "\\" + strlibfile, i);
            }
            int cnt = 0;
            String strFilename = dlg.FileName;
            listStrSudoku.Items.Add(DateTime.Now.ToShortTimeString());
            listStrSudoku.Items.Add("导入文件"+strFilename+"包含错误：");
            StreamReader sr = new StreamReader(strFilename, Encoding.GetEncoding("GB2312"));
            String strContent = sr.ReadToEnd();
            sr.Close();
            strContent=strContent.Replace("\r", "\n").Replace("\n\n","\n");
            String[] strs = strContent.Split(new char[] { '\n' });
            foreach (String strSudoku in strs)
            {
                if (strSudoku.Length != 81)
                {
                    listStrSudoku.Items.Add("ErrorLength:"+strSudoku);
                    continue;
                }
                if (dso.solution_count(strSudoku) == 1)
                {
                    int lvl = get_lvl(strSudoku);
                    if(lvl == 0 ) continue;//避免输入里有无解题
                    sls[lvl - 1].insertSudoku(strSudoku, true);
                    ++cnt;
                }
                else
                {
                    listStrSudoku.Items.Add("MultiSolutions:"+strSudoku);
                }
            }
            labelInfo.Text = "一共成功导入" + cnt + "个题目";
            listStrSudoku.Items.Add("一共成功导入" + cnt + "个题目");
            listStrSudoku.Items.Add(DateTime.Now.ToShortTimeString());
        }

    }
    
    public class SudokuLibrary : IEnumerable
    {
        private XmlDocument xmlDoc = new XmlDocument();
        private XmlNode root = null;
        private String strLibraryFile = "";
        private int _lvl = 0;
        
        public readonly int library_version = 1;

        public SudokuLibrary(String strLibraryFile,int lvl)
        {
            this.strLibraryFile = strLibraryFile;
            _lvl = lvl;
            Reload();
        }
        public static void removeSudokuPrefix(ref String strSudoku)
        {
            if(strSudoku.StartsWith("KLSUDOKU"))
            {
                strSudoku=strSudoku.TrimStart("KLSUDOKU".ToCharArray());
            }
        }
        public void Save()
        {
            xmlDoc.Save(strLibraryFile);
        }
        public void Reload()
        {
            if (File.Exists(strLibraryFile))
            {
                try
                {
                    xmlDoc.Load(strLibraryFile);
                }
                catch(Exception)
                {
                    xmlDoc = new XmlDocument();
                }
            }
            else
                xmlDoc = new XmlDocument();
            root = xmlDoc.SelectSingleNode("lvl");
            //应该有版本属性            
            if(root!=null)
            {
                //判断版本是否是支持的，不支持就清除掉
                String strVersion = ((XmlElement)root).GetAttribute("library_version");
                if (strVersion == "" || pubfun.str2int(strVersion) != library_version)
                {
                    xmlDoc = new XmlDocument();
                    root = null;
                }
            }
            if (root == null)
            {
                XmlElement xe = xmlDoc.CreateElement("lvl");
                xe.SetAttribute("value", _lvl.ToString());
                xe.SetAttribute("library_version", library_version.ToString());
                root = xe;
                xmlDoc.AppendChild(root);
            }

        }
        public XmlNode insertSudoku(String strSudoku,bool save)
        {
            XmlNode xn = xmlDoc.CreateElement("KLSUDOKU"+strSudoku);
            root.AppendChild(xn);
            if (save)
                Save();
            return xn;
        }
        public XmlNode getSudoku(String strSudoku)
        {
            if (this.Count < 1)
                throw new Exception("sudoku library has no sudoku!");
            return root.SelectSingleNode("KLSUDOKU" + strSudoku);
        }

        public XmlNode randGetSudoku()
        {
            if (this.Count < 1)
                throw new Exception("sudoku library has no sudoku!");
            Random ro = new Random();
            int id = ro.Next(0, this.Count - 1);
            return root.ChildNodes[id];
        }

        public bool removeSudoku(XmlNode xn)
        {
            try
            {
                root.RemoveChild(xn);
                return true;
            }
            catch(Exception e)
            {
                Debug.WriteLine(e.Message);
                return false;
            }
        }

        public int Count{
            get{
                return root.ChildNodes.Count;
            }
        }
        public IEnumerator GetEnumerator()
        {
            foreach (XmlNode xn in root)
            {
                yield return xn;
            }
        }

    }

    public class ConfigInfo
    {
        private ConfigInfo() { }
        public static readonly String[] lvlList = new String[] { "容易", "普通", "困难", "极难", "骨灰" };
        /*直观法的难度感觉与逻辑解法的难度感觉是不一样的
         * 同行，同列，同九宫的唯一数是最简单的
         * 综合了行列和九宫的显式唯一数反而有点难
         * 
         * */
        private XmlDocument xmlDoc;
        private XmlNode root = null;
        private String strXmlFilename;
        private Hashtable m_props = new Hashtable();
        private Hashtable m_toplist = new Hashtable();//排行榜数据
        private static ConfigInfo m_instance = null;

        public static ConfigInfo Instance()
        {
            if (m_instance == null) //非线程安全的实现
            {
                m_instance = new ConfigInfo();
                m_instance.m_props["workpath"] = System.Windows.Forms.Application.StartupPath;// System.Environment.CurrentDirectory;
                m_instance.Load();
                m_instance.StartThread();
            }
            return m_instance;
        }

        public void notifyWorking(bool workflag)
        {
            working = workflag;
        }

        public void notifyExit()
        {
            Save();
            if (chkThread.IsAlive)
            {
                stopThread = true;
                chkThread.Join();
            }
        }
        //后台题库生成程序
        private Thread chkThread;
        private volatile bool stopThread = false;
        public volatile bool working = false;
        protected void StartThread()
        {
            chkThread = new Thread(new ThreadStart(threadMain));
            //chkThread.Start();
        }
        private void threadMain()
        {
            this["build_sudoku_library"] = this["build_sudoku_library", "1"];
            if (this["build_sudoku_library", "1"] == "0")
            {
                Debug.WriteLine("配置项[build_sudoku_library]被设置为0，不自动构建难度题库");
                stopThread = true;
                return;
            }

            int c = 0;
            while (!stopThread && c++ < 100)
                Thread.Sleep(100);//不要马上启动这个线程，避免启动时太多工作

            SudokuLibrary[] sls = new SudokuLibrary[lvlList.Length];
            for (int i = 1; i <= lvlList.Length; ++i)
            {
                String strlibfile = this["lvl_" + i + "_xml", "lvl" + i + ".xml"];
                sls[i - 1] = new SudokuLibrary(m_props["workpath"] + "\\" + strlibfile, i);
            }
            //载入各个难度库，确保每个库至少有N道题
            int n = pubfun.str2int(this["sl_num", "100"]);
            int s = pubfun.str2int(this["sl_check_interval", "10000"]);
            const int m = 50;
            int sc = s / 100;//0.1秒内响应退出
            SudokuGenerator gen = new SudokuGenerator();
            try
            {
                while (!stopThread)
                {
                    //一分钟内最多生成50题
                    Debug.WriteLine("\r\n开始生成题目...");
                    for (int i = 0; i < sls.Length; ++i)
                    {
                        c = 0;
                        if (sls[i].Count < n)
                        {
                            while (sls[i].Count < n && c++ < m)
                            {
                                //生成题目，得到难度系数，然后放置到相应的题库里
                                if (stopThread)
                                    throw new Exception("thread has recv stop signal!");
                                Thread.Sleep(10);
                                if (!working)//只有在前台不忙的时候才工作，不然单CPU上太过影响感觉
                                    break;
                                String res = "";
                                String strSudoku = gen.do_dlx_generator(ref res);
                                puzzle pz = new puzzle();
                                pz.loadPuzzle(strSudoku.ToCharArray());
                                solver so = new solver(pz);
                                so.strResult = res;//避免遇到猜数的情况
                                so.background = true;
                                so.do_solve();
                                int lvl = 0;
                                foreach (operation.SolveHelper helper in Enum.GetValues(typeof(operation.SolveHelper)))
                                {
                                    foreach (List<operation> oplist in so.opsteps)
                                    {
                                        foreach (operation op in oplist)
                                        {
                                            if (operation.lvl(op.helper) > lvl)
                                                lvl = operation.lvl(op.helper);
                                        }
                                    }
                                }
                                if (lvl < sls.Length)
                                    sls[(int)lvl].insertSudoku(strSudoku, true);
                                else
                                    sls[sls.Length - 1].insertSudoku(strSudoku, true);
                                if (sls[0].Count < n && lvl > 1)
                                {
                                    //为最容易的题目降级
                                    dlx_solver dso = new dlx_solver();
                                    puzzle respz = new puzzle();
                                    respz.loadPuzzle(res);
                                    while (true)
                                    {
                                        bool change = false;
                                        pz.loadPuzzle(strSudoku);
                                        foreach (List<operation> oplist in so.opsteps)
                                        {
                                            operation op = oplist[0];
                                            if (operation.lvl(op.helper) > 1)
                                            {
                                                Point p = op.m_points[0];
                                                pz[p] = respz[p];
                                                change = true;
                                            }
                                        }
                                        if (!change)//应该是知道再也没有lvl大于1的步骤（change==false）的时候，才存储
                                        {
                                            sls[0].insertSudoku(strSudoku, true);
                                            break;
                                        }
                                        strSudoku = pz.exportPuzzleCompact();
                                        if (dso.solution_count(strSudoku) != 1)
                                        {
                                            throw new Exception("has error here!");
                                        }
                                        res = dso.do_solve(strSudoku);
                                        so = new solver(pz);
                                        so.do_solve();
                                    }
                                }
                            }
                            sls[i].Save();
                            if (c > m)
                                break;//完成m题后，等下一次机会
                        }
                        if (stopThread)
                            throw new Exception("thread has recv stop signal!");
                    }
                    for (int i = 0; i < sc; ++i)
                    {
                        if (stopThread)
                            throw new Exception("thread has recv stop signal!");
                        Thread.Sleep(100);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        public String this[String index]
        {
            get
            {
                return this[index, ""];
            }
            set
            {
                this[index, ""] = value;
            }
        }

        public String this[String index, String Default]
        {
            get
            {
                if (m_props.ContainsKey(index))
                    return (String)m_props[index];
                return Default;
            }
            set
            {
                m_props[index] = value;
            }
        }
        public void Save()
        {
            saveConfigToXML();
        }
        public void Load()
        {
            strXmlFilename = m_props["workpath"] + "\\Config.xml";
            xmlDoc = new XmlDocument();
            if (File.Exists(strXmlFilename))
            {
                xmlDoc.Load(strXmlFilename);
            }
            loadConfigFromXML();
        }

        public void saveConfigToXML()
        {
            foreach (DictionaryEntry de in m_props)
            {
                String key = (String)de.Key;
                XmlNodeList xnl = xmlDoc.GetElementsByTagName(key);
                if (xnl.Count == 0)//新增加的
                {
                    XmlElement xe = xmlDoc.CreateElement(key);
                    root.AppendChild(xe);
                    xe.SetAttribute("value", (String)de.Value);
                }
                else
                {
                    foreach (XmlElement xe in xnl)
                    {
                        if (xe.NodeType == XmlNodeType.Element)
                        {
                            xe.SetAttribute("value", (String)de.Value);
                            break;
                        }
                    }
                }
            }
            xmlDoc.Save(strXmlFilename);
        }

        //检查并设置默认值（应该先检查）
        private XmlNode initDefaultConfig()
        {
            //重新创建缺省配置
            XmlNode root = xmlDoc.CreateElement("Config");
            xmlDoc.AppendChild(root);
            //缺省难度
            XmlElement xe = xmlDoc.CreateElement("lvl");
            root.AppendChild(xe);
            xe.SetAttribute("value", "1");
            //缺省显示候选数
            xmlDoc.Save(strXmlFilename);
            //题库文件缺省名称
            for (int i = 1; i <= ConfigInfo.lvlList.Length; ++i)
            {
                String name = "lvl_" + i + "_xml";
                String value = "lvl" + i + ".xml";
                xe = xmlDoc.CreateElement(name);
                root.AppendChild(xe);
                xe.SetAttribute("value", value);
            }

            return root;
        }

        public void loadConfigFromXML()
        {
            try
            {
                root = xmlDoc.SelectSingleNode("Config");//查找<Config>
            }
            catch (Exception)
            {
            }
            if (root == null)
            {
                root = initDefaultConfig();
                saveConfigToXML();
            }

            foreach (XmlElement xe in root.ChildNodes)
            {
                m_props[xe.Name] = xe.GetAttribute("value");
            }
        }
    }
}
